#include "HeadFile.h"

#include <filesystem>
#include <fstream>
#include <cassert>
#include <iostream>

#include "TokenGenerator.h"

#include "ClassGenerator.h"
#include "EnumGenerator.h"

C32String ReadToString(const std::filesystem::path & FilePath)
{
    std::ifstream File(FilePath);
    if (!File.is_open())
    {
        assert(false);
        return C32String();
    }

    std::u8string FileContent((std::istreambuf_iterator<char>(File)),
        (std::istreambuf_iterator<char>()));
    return C32String(FileContent.c_str());

}

void CHeadFile::GenerateMetaFileContent()
{
    GenerateHeadFileContent();
    GenerateCppFileContent();

}

void CHeadFile::ParseFile()
{
    //read file
    Content = ReadToString(Path);


    //generate token
    CTokenGenerator TokenGenerator(this);
    TokenGenerator.GenerateTokens();


    while(true)
    {
        auto Token = GetCurrentToken();

        switch(Token->Type)
        {
            case ETokenType::RCLASS:
            case ETokenType::RSTRUCT:
            case ETokenType::RINTERFACE:
            {
                CClassGenerator ClassGenerator(this);
                ClassGenerator.Generate();
                auto Class=ClassGenerator.ClassInfo;
                if(Class->IsStruct())
                {
                    bHaveStruct=true;
                }
                else if(Class->IsClass())
                {
                    bHaveClass=true;
                }
                else if(Class->IsInterface())
                {
                    bHaveInterface=true;
                }

                Classes.push_back(Class);

                break;
            }
            case ETokenType::RENUM:
            {
                CEnumGenerator EnumGenerator(this);
                EnumGenerator.Generate();
                Enums.push_back(EnumGenerator.EnumInfo);
                break;
            }

            default:
            {
                GotoNextToken();
            }
            
        }
        
        if( TokenIndex>=Tokens.size())
        {
            break;
        }

    }    
}

std::shared_ptr<CToken> CHeadFile::GetCurrentToken()
{
    return Tokens[TokenIndex];
}

void CHeadFile::GotoNextToken()
{
    TokenIndex++;
}

void CHeadFile::RaiseError(SFilePosition InFilePosition, const C32String &InMessage)
{
    //just print error message
    std::cout
        <<"Error: "<<(const char* )InMessage.ToUtf8().data()
        <<" at "
        << Path.string()
        <<" Line:"
        <<InFilePosition.Line
        <<" Column:"<<InFilePosition.Column
        <<" Num:"<<InFilePosition.CharNum
        <<std::endl;

    throw std::runtime_error("Engine Build Tool Error");

}

void CHeadFile::RaiseError(std::size_t InLineIndex, std::size_t InColumnIndex, std::size_t InCharNum, const C32String &InMessage)
{
    RaiseError(SFilePosition(InLineIndex, InColumnIndex, InCharNum), InMessage);
}

void CHeadFile::RaiseError(std::shared_ptr<CToken> InToken, const C32String &InMessage)
{
    RaiseError(InToken->FilePosition, InMessage);
}

void CHeadFile::RaiseFileError(const std::u8string &InMessage)
{
    std::cout
        <<"Error: "<<(const char* )InMessage.data()
        <<" at "
        << Path.string()
        <<std::endl;  
    
    throw std::runtime_error("Engine Build Tool Error");  
}

void CHeadFile::GenerateHeadFileContent()
{
    if(Classes.empty()&& Enums.empty() )
    {
        return;
    }
    
    HeadFileContent += u8"//This file is generated by BuildTool,do not modify it manually\n";
    HeadFileContent += u8"#pragma once\n";
    HeadFileContent += u8"#include \"Reflection/Reflection.h\"\n";

    if(bHaveStruct)
    {
        HeadFileContent +=u8"#include \"Reflection/Type.h\"\n";
    }

    if(bHaveClass)
    {
        HeadFileContent += u8"#include \"Reflection/Class.h\"\n";
        HeadFileContent += u8"#include \"Reflection/Property.h\"\n";
        HeadFileContent += u8"#include \"Containers/SharedObjectPtr.h\"\n";
        HeadFileContent += u8"#include \"Containers/WeakObjectPtr.h\"\n";
    }


    if(!Enums.empty())
    {
        HeadFileContent += u8"#include \"Reflection/Enum.h\"\n";
    }

    //generate file id
    //convert file path to string
    std::u8string FileIDString;
    {
        std::filesystem::path CurrentPath=std::filesystem::absolute(Path);

        //read file name
        {
            std::u8string FileRawName=Path.filename().u8string();
            //replace all non alpha numeric character to _
            for(auto Char:FileRawName)
            {
                if(std::isalnum(Char))
                {
                    FileIDString+=Char;
                }
                else
                {
                    FileIDString+=u8"_";
                }
            }
        }
        CurrentPath=CurrentPath.parent_path();

        //read folder name
        while(CurrentPath!=CurrentPath.root_path())
        {
            std::u8string FolderName=CurrentPath.filename().u8string();
            //replace all non alpha numeric character to _

            std::u8string FinalFolderName;
            for(auto Char:FolderName)
            {
                if(std::isalnum(Char))
                {
                    FinalFolderName+=Char;
                }
                else
                {
                    FinalFolderName+=u8"_";
                }
            }

            FileIDString= FinalFolderName+ u8"_" + FileIDString;

            CurrentPath=CurrentPath.parent_path();
        }
    }


    HeadFileContent += u8"#undef FILE_ID\n";
    HeadFileContent += u8"#define FILE_ID "+FileIDString +u8" \n";

    for(auto&Class : Classes)
    {   
        if(Class->IsClass())
        {
           
            if(Class->ImplementedInterfaces.size()>0)
            {
                HeadFileContent += u8"#include \"Containers/SharedInterfacePtr.h\"\n";
            }

            HeadFileContent += u8"class CNativeClass;\n";
            

            std::u8string ReflectorClassName=Class->Name+u8"_Reflector";

            HeadFileContent += u8"class "+ReflectorClassName+u8"\n";
            HeadFileContent += u8"{\n";
            HeadFileContent += u8"public:\n";
            HeadFileContent += u8"  static CNativeClass* GetStaticClass();\n";
            HeadFileContent += u8"  static CNativeClass* GenerateClass();\n";
            HeadFileContent += u8"  static void PostConstructField();\n";        
            HeadFileContent += u8"};\n";

            HeadFileContent += u8"#define ";
            HeadFileContent += FileIDString+u8"_"+std::u8string((char8_t*)std::to_string(Class->RCLASSLineNumber+1).c_str())+u8"_Class \\\n";

            HeadFileContent += u8"    class "+Class->Name+u8";\\\n";
            HeadFileContent += u8"    using "+Class->Name+u8"Ptr=TSharedObjectPtr<"+Class->Name+u8">;\\\n";
            HeadFileContent += u8"    using "+Class->Name+u8"WeakPtr=TWeakObjectPtr<"+Class->Name+u8">;\\\n";
            

            // HeadFileContent += u8"    template<>\\\n";
            // HeadFileContent += u8"    class CGetType<"+Class->Name+u8"*>\\\n";
            // HeadFileContent += u8"    {\\\n";
            // HeadFileContent += u8"      public:\\\n";
            // HeadFileContent += u8"          CType* operator () ();\\\n";
            // HeadFileContent += u8"    };\n";
            
            HeadFileContent += u8"\n";

            HeadFileContent += u8"#define ";
            HeadFileContent += FileIDString+u8"_"+std::u8string((char8_t*)std::to_string(Class->ROBJECTLineNumber+1).c_str())+u8"_Object \\\n";

            //HeadFileContent += u8"#define ROBJECT \\\n";
            HeadFileContent += u8"public: \\\n";
            HeadFileContent += u8"using Super="+Class->BaseClassName+u8"; \\\n";
            HeadFileContent += u8"static CClass* StaticClass(); \\\n";
            HeadFileContent += u8"virtual CClass* GetClass() const override; \\\n";
            HeadFileContent += u8"static CType*  StaticSharedPtrType(); \\\n";
            HeadFileContent += u8"static CType*  StaticWeakPtrType(); \\\n";
            HeadFileContent += u8"virtual CType* GetSharedPtrType() const override; \\\n";
            HeadFileContent += u8"virtual CType* GetWeakPtrType() const override; \\\n";
            HeadFileContent += u8"friend class "+ReflectorClassName+u8"; \\\n";
            if(Class->ImplementedInterfaces.size()>0)
            {
                HeadFileContent += u8"TSharedObjectPtr<RObject> _GetObjectOfInterface() override;\\\n";

                for(auto& Interface:Class->ImplementedInterfaces)
                {
                    //remove I prefix
                    auto InterfaceWithoutPrefix=Interface.substr(1);
                    HeadFileContent += u8"TSharedInterfacePtr<"+Interface+u8"> To"+InterfaceWithoutPrefix+u8"();\\\n"; 
                }
            }
            HeadFileContent += u8"\n\n";

            HeadFileContent += u8"class "+Class->Name+u8";\n";
            HeadFileContent += u8"template<> class ";
            if(Class->ExportMacro)
            {
                HeadFileContent += Class->ExportMacro.value();
                HeadFileContent += u8" ";
            }
            HeadFileContent+= u8"CGetType<"+Class->Name+u8">\n";
            HeadFileContent += u8"{\n";
            HeadFileContent += u8"public:\n";
            HeadFileContent += u8"    CType* operator () ();\n";
            HeadFileContent += u8"};\n";
        }
        else if( Class->IsStruct() )
        {
            HeadFileContent += u8"class CNativeStrut;\n";

            std::u8string ReflectorClassName=Class->Name+u8"_Reflector";

            HeadFileContent += u8"class "+ReflectorClassName+u8"\n";
            HeadFileContent += u8"{\n";
            HeadFileContent += u8"public:\n";
            HeadFileContent += u8"  static CNativeStrut* StaticStruct();\n";
            HeadFileContent += u8"  static void PostConstructField();\n";
            HeadFileContent += u8"};\n";

            HeadFileContent += u8"struct "+Class->Name+u8";\n";
            HeadFileContent += u8"template<> class ";
            if(Class->ExportMacro)
            {
                HeadFileContent += Class->ExportMacro.value();
                HeadFileContent += u8" ";
            }
            HeadFileContent+= u8"CGetType<"+Class->Name+u8">\n";
            HeadFileContent += u8"{\n";
            HeadFileContent += u8"public:\n";
            HeadFileContent += u8"    CType* operator () ();\n";
            HeadFileContent += u8"};\n";

            HeadFileContent += u8"#define ";
            HeadFileContent += FileIDString+u8"_"+std::u8string((char8_t*)std::to_string(Class->ROBJECTLineNumber+1).c_str())+u8"_Object \\\n";

            HeadFileContent += u8"public: \\\n";
            HeadFileContent += u8"static CNativeStrut* StaticStruct(); \\\n";
            HeadFileContent += u8"CNativeStrut* GetStruct(); \\\n";
            HeadFileContent += u8"friend class "+ReflectorClassName+u8"; \n";
            HeadFileContent += u8"\n";
        }
        else if(Class->IsInterface())
        {
            std::u8string ReflectorClassName=Class->Name+u8"_Reflector";
            HeadFileContent += u8"class "+ReflectorClassName+u8"\n";
            HeadFileContent += u8"{\n";
            HeadFileContent += u8"public:\n";
            HeadFileContent += u8"  static CInterface* StaticInterface();\n";
            HeadFileContent += u8"  static void PostConstructField();\n";
            HeadFileContent += u8"};\n";

            HeadFileContent += u8"class "+Class->Name+u8";\n";
            HeadFileContent += u8"template<> class ";
            if(Class->ExportMacro)
            {
                HeadFileContent += Class->ExportMacro.value();
                HeadFileContent += u8" ";
            }
            HeadFileContent+= u8"CGetType<"+Class->Name+u8">\n";
            HeadFileContent += u8"{\n";
            HeadFileContent += u8"public:\n";
            HeadFileContent += u8"    CType* operator () ();\n";
            HeadFileContent += u8"};\n";

            HeadFileContent += u8"#define ";
            HeadFileContent += FileIDString+u8"_"+std::u8string((char8_t*)std::to_string(Class->ROBJECTLineNumber+1).c_str())+u8"_Object \\\n";

            HeadFileContent += u8"public: \\\n";
            HeadFileContent += u8"static CInterface* StaticInterface(); \\\n";
            HeadFileContent += u8"CInterface* GetInterface(); \\\n";
            HeadFileContent += u8"friend class "+ReflectorClassName+u8"; \n";
            HeadFileContent += u8"\n";

        }
    }

    if(!Enums.empty())
    {
        for(auto Enum: Enums)
        {
            std::u8string ExportMacro=u8"";
            if(Enum->Meta.contains(u8"ExportMacro"))
            {
                ExportMacro=std::get<std::u8string>(Enum->Meta[u8"ExportMacro"].value());
            }
            
            HeadFileContent += u8"enum class "+Enum->Name+u8";\n";
            HeadFileContent += u8"template<> class ";
            HeadFileContent += ExportMacro;
            HeadFileContent += u8" CGetType<"+Enum->Name+u8">\n";
            HeadFileContent += u8"{\n";
            HeadFileContent += u8"public:\n";
            HeadFileContent += u8"    CType* operator () ();\n";
            HeadFileContent += u8"};\n";
        }  
    
    }


      
}

void CHeadFile::GenerateCppFileContent()
{
    if(Classes.empty()&& Enums.empty() )
    {
        return;
    }
    
    CppFileContent += u8"//This file is generated by BuildTool,do not modify it manually\n";
    CppFileContent += u8"//namespace "+Namespace+u8"\n";
    //add all head files
    CppFileContent += u8"#include \"Reflection/FunctionType.h\"\n";
    

    if(bHaveStruct)
    {
        CppFileContent += u8"#include \"Reflection/NativeStruct.h\"\n";   
        CppFileContent += u8"#include \"Reflection/TypeUtils.h\"\n";
        CppFileContent += u8"#include \"Reflection/Reflection.h\"\n";
        CppFileContent += u8"#include \"Reflection/Property.h\"\n";
        CppFileContent += u8"#include \"Reflection/PropertyList.h\"\n";
        CppFileContent += u8"#include \"Reflection/NativeFieldProperty.h\"\n";
        CppFileContent += u8"#include \"Reflection/FunctionProperty.h\"\n";
        CppFileContent += u8"#include \"Containers/Any.h\"\n";
        CppFileContent += u8"#include \"Reflection/NativeFunction.h\"\n";
        CppFileContent += u8"#include \"Reflection/FunctionList.h\"\n";
    }

    if(bHaveClass)
    {
        CppFileContent += u8"#include <cstddef>\n";
        CppFileContent += u8"#include <memory>\n";
        CppFileContent += u8"#include \"Containers/String.h\"\n";
        CppFileContent += u8"#include \"Containers/SharedObjectPtr.h\"\n";
        CppFileContent += u8"#include \"Containers/Any.h\"\n";
        CppFileContent += u8"#include \"Reflection/PropertyList.h\"\n";
        CppFileContent += u8"#include \"Reflection/FunctionList.h\"\n";
        CppFileContent += u8"#include \"Reflection/TypeUtils.h\"\n";
        CppFileContent += u8"#include \"Reflection/ObjectPtr.h\"\n";
        CppFileContent += u8"#include \"Reflection/ClassImplement.h\"\n";
        CppFileContent += u8"#include \"Reflection/NativeFieldProperty.h\"\n";
        CppFileContent += u8"#include \"Reflection/FunctionProperty.h\"\n";
        CppFileContent += u8"#include \"Reflection/NativeFunction.h\"\n";

        CppFileContent += u8"#include \"ScriptRuntime/Type/ScriptContent.h\"\n";
        CppFileContent += u8"#include \"ScriptRuntime/Type/GSharpClass.h\"\n";

    }

    
    if(bHaveInterface)
    {
        CppFileContent += u8"#include \"Reflection/InterfaceImplement.h\"\n";
        CppFileContent += u8"#include \"Containers/SharedInterfacePtr.h\"\n";
        CppFileContent += u8"#include \"Reflection/TypeUtils.h\"\n";
        CppFileContent += u8"#include \"Reflection/Reflection.h\"\n";
        CppFileContent += u8"#include \"Reflection/Property.h\"\n";
        CppFileContent += u8"#include \"Reflection/PropertyList.h\"\n";
        CppFileContent += u8"#include \"Reflection/NativeFieldProperty.h\"\n";
        CppFileContent += u8"#include \"Reflection/FunctionProperty.h\"\n";
        CppFileContent += u8"#include \"Containers/Any.h\"\n";
        CppFileContent += u8"#include \"Reflection/NativeFunction.h\"\n";
        CppFileContent += u8"#include \"Reflection/FunctionList.h\"\n";
    }

    if(!Enums.empty())
    {
        CppFileContent += u8"#include \"Reflection/NativeEnum.h\"\n";
    }

    for(auto&Class :Classes)
    {
        //add extra head files
        if(Class->Meta.contains(u8"ExtraHeader"))
        {
            auto DependenceHeader=Class->Meta[u8"ExtraHeader"];
            if(DependenceHeader.has_value())
            {
                std::visit([&](auto&& arg)
                {
                    using T = std::decay_t<decltype(arg)>;
                    if constexpr (std::is_same_v<T, std::u8string>)
                    {
                        CppFileContent += u8"#include \""+arg+u8"\"\n";
                    }
                    else if constexpr (std::is_same_v<T, std::vector<std::u8string>>)
                    {
                        for(auto Header:arg)
                        {
                            CppFileContent += u8"#include \""+Header+u8"\"\n";
                        }
                    }
                }, DependenceHeader.value());
            }
        }
    }


    CppFileContent += u8"#include \""+Path.filename().u8string()+u8"\"\n";

    for(auto&Class :Classes)
    {
    
      
        std::u8string ReflectorClassName=Class->Name+u8"_Reflector";
        std::u8string AllFunctions;

        //add default constructor for script
        {
            AllFunctions += u8"    {\n";
            AllFunctions += u8"        CType* ReturnType=CGetType<void>()();\n";
            AllFunctions += u8"        TVector<SDecoratedType> AllParameterTypes;\n";
            AllFunctions += u8"        CNativeFunction* NativeFunction = new CNativeFunction(Field->Functions,CFunction::GetConstructorName(),SDecoratedType(ReturnType),AllParameterTypes,{});\n";
            AllFunctions += u8"        NativeFunction->SetCppBody([](const TVector<CAny>& InArgs)->CAny\n";
            AllFunctions += u8"        {\n";
            AllFunctions += u8"            //do nothing\n";
            AllFunctions += u8"            return CAny();\n";
            AllFunctions += u8"        });\n";
            AllFunctions += u8"    }\n";
        }


        for(auto& Function:Class->Functions )
        {
            AllFunctions += u8"    {\n";
            AllFunctions += u8"        SDecoratedType ReturnType=SDecoratedType(WH::TReflectionType<"+ Function->ReturnType+u8">::FindOrCreateType());\n";
            AllFunctions += u8"        TVector<SDecoratedType> AllParameterTypes;\n";
            for(std::size_t ParameterIndex=0; ParameterIndex<Function->ParameterTypes.size();ParameterIndex++ )
            {
                AllFunctions+=u8"        AllParameterTypes.Add(SDecoratedType(WH::TReflectionType<"+Function->ParameterTypes[ParameterIndex]+u8">::FindOrCreateType()));\n";
            }       

            std::u8string MetaString;
            bool NotInScript= Function->Meta.contains(u8"NotInScript");
            if(NotInScript)
            {
                MetaString+=u8"{EPropertyFlags::P_NotInScript,true},";
            }

            std::u8string FunctionScriptName=u8"U\""+Function->Name+u8"\"";
            if(Function->Meta.contains(u8"ScriptConstructor"))
            {
                FunctionScriptName=u8"CFunction::GetConstructorName()";
            }

            AllFunctions += u8"        CNativeFunction* NativeFunction = new CNativeFunction(Field->Functions,"+FunctionScriptName+u8",ReturnType,AllParameterTypes,{"+MetaString+u8"});\n";

            if(!NotInScript)
            {
                AllFunctions += u8"        assert(NativeFunction->IsTypeSupportScript() && \"if function cant use in script, must add flag [ NotInScript ] \" );\n" ;
            }

            if(Function->IsStatic)
            {
                AllFunctions += u8"        NativeFunction->SetStatic();\n";
            }

            if(Function->IsVirtual)
            {
                AllFunctions += u8"        NativeFunction->MarkAsVirtual();\n";
                AllFunctions += u8"        Field->Functions->VirtualFunctionTable.Add(NativeFunction->ID);\n";
            }
            AllFunctions+=u8"        {\n";
            AllFunctions+=u8"           "+Function->ReturnType;
            if(!Function->IsStatic)
            {
                AllFunctions+=u8"("+Class->Name+u8"::*RawPointer)(";
            }
            else
            {
                AllFunctions+=u8"(*RawPointer)(";
            }
            for(int64_t ParameterIndex=0;ParameterIndex<Function->DecoratedParameterTypes.size();ParameterIndex++)
            {
                if(ParameterIndex!=0)
                {
                    AllFunctions+=u8",";
                }
                AllFunctions+=Function->DecoratedParameterTypes[ParameterIndex];
            }
            AllFunctions+=u8")";
            if(Function->IsConst)
            {
                AllFunctions+=u8" const";
            }
            AllFunctions+=u8"=&"+Class->Name+u8"::"+Function->Name+u8";\n";
            AllFunctions+=u8"           NativeFunction->NativeFunctionPtr=*(int64_t*)(&RawPointer);\n";
            AllFunctions+=u8"        }\n";
            AllFunctions+=u8"        NativeFunction->SetCppBody([](const TVector<CAny>& InArgs)->CAny\n";
            AllFunctions+=u8"        {\n";
            
            if(!Function->IsStatic)
            {
                if(Class->IsStruct())
                {
                    AllFunctions+=u8"           auto Self= *reinterpret_cast<"+Class->Name+u8"**>(InArgs[0].GetPointer());\n";
                }
                else if(Class->IsInterface())
                {
                    AllFunctions+=u8"           auto Self= AnyCast<TSharedInterfacePtr<"+Class->Name+u8">>(InArgs[0]);\n";
                }
                else
                {
                    AllFunctions+=u8"           auto Self= AnyCast<TSharedObjectPtr<"+Class->Name+u8">>(InArgs[0]);\n";
                }
            }
            
            bool bHaveReturn=false;

            if(Function->ReturnType!=u8"void")
            {
                AllFunctions+=u8"            return \n";
                bHaveReturn=true;
            }

            if(Function->IsStatic)
            {
                AllFunctions+=u8"            "+Class->Name+u8"::";
            }
            else
            {
                AllFunctions+=u8"            Self->";
            }

            AllFunctions+=Function->Name+u8"(";
         

            for(std::size_t ParameterIndex=0; ParameterIndex<Function->ParameterTypes.size();ParameterIndex++ )
            {
                if(ParameterIndex!=0)
                {
                    AllFunctions+=u8",";
                }

                std::size_t ArgIndex=Function->IsStatic? ParameterIndex : ParameterIndex+1;

                AllFunctions+=u8"AnyCast<"+Function->ParameterTypes[ParameterIndex]+u8">(InArgs["+(char8_t*)std::to_string(ArgIndex).c_str()+u8"])";
            }
            AllFunctions+=u8");\n";

            if(!bHaveReturn)
            {
                AllFunctions+=u8"            return CAny();\n";
            }

            AllFunctions+=u8"        });\n";
            AllFunctions+= u8"    }\n";

        }

        std::u8string AllFields;
        for(auto& Field: Class->Fields)
        {

            AllFields+= u8"    {\n";
            AllFields+= u8"     auto Property=new CNativeFieldProperty";

            std::u8string FieldName=Field->Name;
            
            std::u8string MetaString;//=u8"{EPropertyFlags::P_Name,CString(U\""+FieldName+u8"\")}";

            if(Field->Meta.contains(u8"Saveable"))
            {
                MetaString+=u8"{EPropertyFlags::P_Saveable,true},";
            }

            if(Field->Meta.contains(u8"NoSerialize"))
            {
                MetaString+=u8"{EPropertyFlags::P_NoSerialize,true},";
            }

            if(Field->Meta.contains(u8"SerializeAsBinary"))
            {
                MetaString+=u8"{EPropertyFlags::P_SerializeAsBinary,true},";
            }
            

            bool NotInScript= Field->Meta.contains(u8"NotInScript");
            if(NotInScript)
            {
                MetaString+=u8"{EPropertyFlags::P_NotInScript,true},";
            }


            if(Field->Meta.contains(u8"Name"))
            {
                MetaString+=u8"{EPropertyFlags::P_Name,CString(U\""+
                std::get<std::u8string>(Field->Meta[u8"Name"].value())+u8"\")},"
                ;
            }

            if(Field->Meta.contains(u8"DisplayInner"))
            {
                MetaString+=u8"{EPropertyFlags::P_DisplayInner,true},";
            }

            

            AllFields += u8"(Field->Properties,{"+MetaString+u8"}";
            
            //add field type
            AllFields += u8",WH::TReflectionType<"+Field->Type+u8">::FindOrCreateType(),U\""+ FieldName + u8"\"" ;

            //add offset
            {
                AllFields += u8",offsetof("+Class->Name+u8","+Field->Name+u8"),";
                AllFields += u8"Field->Properties->GetNonStaticFieldProperties().Num()";
            }
            AllFields+= u8");\n";

            if(!NotInScript)
            {
                AllFields += u8"        assert(Property->IsTypeSupportScript() && \"if field cant use in script, must add flag [ NotInScript ] \" );\n" ;
            }

            AllFields+= u8"    Field->Properties->AddProperty(Property);\n";
            AllFields+= u8"    }\n";
        }        
        
        std::u8string AllProperties;
        for(auto& Property: Class->Properties )
        {
            // format type as decltype(((ClassName*)(new int()))->GetterFunctionName())
            Property->Type= u8"decltype((("+Class->Name+u8"*)(new int()))->"+Property->GetterFunctionName+u8"())"; 

            AllProperties+=u8"    {\n";

            std::u8string MetaString; 

            bool NotInScript= Property->Meta.contains(u8"NotInScript");
            if(NotInScript)
            {
                MetaString+=u8"{EPropertyFlags::P_NotInScript,true},";
            }


            AllProperties+= u8"        auto Property=new CFunctionProperty(Field->Properties,{"+MetaString+u8"},";

            //add getter function
            AllProperties += u8"[](const void* Self)->CAny{ return (("
                +Class->Name
                +u8"*)Self)->"
                +Property->GetterFunctionName+u8"(); }";

            //add setter function
            if(Property->SetterFunctionName.has_value())
            {
                AllProperties += u8",[](void* Self,CAny Value){ (("
                    +Class->Name
                    +u8"*)Self)->"
                    +Property->SetterFunctionName.value()+u8"(AnyCast<"
                    +Property->Type
                    +u8">(Value)); }";
            }
            else
            {
                AllProperties += u8",nullptr";
            }

            //add property type
            AllProperties += u8",SDecoratedType(WH::TReflectionType<"
                +Property->Type
                +u8">::FindOrCreateType()),U\""
                + Property->Name 
                + u8"\");\n" ;

            if(!NotInScript)
            {
                AllProperties += u8"        assert(Property->IsTypeSupportScript() && \"if property cant use in script, must add flag [ NotInScript ] \" );\n" ;
            }

            AllProperties+= u8"        Field->Properties->AddProperty(Property);\n";

            if(Property->Name==u8"StringExpression")
            {
                //make sure StringExpression both have getter and setter
                if(!Property->SetterFunctionName.has_value()
                    || Property->GetterFunctionName.empty())
                {
                    RaiseFileError(u8"StringExpression must have both getter and setter");
                }

                AllProperties+= u8"        Field->Properties->SetStringExpressionProperty(Property);\n";
            }

            AllProperties+= u8"    }\n";

        }
        

        if(Class->IsClass())
        {

            std::u8string ScriptBaseClassName=Class->Name+u8"_SCRIPT_BASE";
            CppFileContent += u8"class "+ScriptBaseClassName+u8":public "+Class->Name+u8"\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"public:\n";
            CppFileContent += u8"    static RObject* CreateInstance(void * InMemory)\n";
            CppFileContent += u8"    {\n";
            CppFileContent += u8"        return new (InMemory) "+ScriptBaseClassName+u8";\n";
            CppFileContent += u8"    }\n";

            //override get class function 
            CppFileContent += u8"    CClass* GetClass() const override\n";
            CppFileContent += u8"    {\n";
            CppFileContent += u8"        return GetScriptContent()->GetClass();\n";
            CppFileContent += u8"    }\n";

            //add virtual function 
            for(std::size_t VirtualFunctionIndex=0;
                VirtualFunctionIndex< Class->VirtualFunctionTable.size();
                 VirtualFunctionIndex++)
            {
                std::shared_ptr<CFunctionInfo> VirtualFunction = Class->VirtualFunctionTable[VirtualFunctionIndex];
                CppFileContent += u8"    "+VirtualFunction->ReturnType+u8" "+VirtualFunction->Name+u8"(";

                for(std::size_t ParameterIndex=0; ParameterIndex<VirtualFunction->ParameterTypes.size();ParameterIndex++ )
                {
                    if(ParameterIndex!=0)
                    {
                        CppFileContent+=u8",";
                    }

                    CppFileContent+=VirtualFunction->ParameterTypes[ParameterIndex]+u8" Arg"+(char8_t*)std::to_string(ParameterIndex).c_str();
                }

                CppFileContent+=u8") override\n";

                CppFileContent+=u8"    {\n";

                CppFileContent+=u8"        SFunctionID FunctionID= GetScriptContent()->GetClass()->Functions->VirtualFunctionTable["
                    + std::u8string((char8_t*)std::to_string(VirtualFunctionIndex).c_str())
                    +u8"];\n";
                
                //check function id
                CppFileContent+=u8"         //check is script function, incase invoke this function\n"; 
                CppFileContent+=u8"         if(!FunctionID.TypeID.IsScriptType())\n";
                CppFileContent+=u8"         {\n";

                if(VirtualFunction->ReturnType ==u8"void")
                {
                    CppFileContent+=u8"            return;\n";
                }
                else
                {
                    CppFileContent+=u8"            return "
                        +VirtualFunction->ReturnType
                        +u8"();\n";
                }
                CppFileContent+=u8"         }\n";
                
                
                CppFileContent+=u8"        auto Function= SFunctionID::FindFunction(FunctionID);\n";

                CppFileContent+=u8"        TVector<CAny> Args;\n";
                CppFileContent+=u8"        Args.Reserve("
                    + std::u8string((char8_t*)std::to_string(VirtualFunction->ParameterTypes.size()+1).c_str())
                    + u8");\n";

                CppFileContent+=u8"        Args.Emplace(GetClass()->SharedPtrType,new TSharedObjectPtr<"
                    +Class->Name
                    +u8">(const_cast<"
                    +ScriptBaseClassName
                    +u8"*>(this)));\n";
                
                //add parameters
                for(std::size_t ParameterIndex=0; ParameterIndex<VirtualFunction->ParameterTypes.size();ParameterIndex++ )
                {
                    CppFileContent+=u8"        Args.Add("
                        +std::u8string(u8"Arg")
                        +(char8_t*)std::to_string(ParameterIndex).c_str()
                        +u8");\n";
                }


                if(VirtualFunction->ReturnType ==u8"void")
                {
                    CppFileContent+=u8"        Function->Invoke(Args);\n";
                }
                else
                {
                    CppFileContent+=u8"        return AnyCast<"
                        +VirtualFunction->ReturnType
                        +u8">(Function->Invoke(Args));\n";
                }

                CppFileContent += u8"    };\n";

            }

            CppFileContent += u8"};\n";

            
            CppFileContent += u8"CNativeClass* "+ReflectorClassName+u8"::GetStaticClass()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"    static CNativeClass* Standalone =GenerateClass();\n"; 
            CppFileContent += u8"    return Standalone;\n";
            CppFileContent += u8"}\n";
            
            CppFileContent += u8"CNativeClass* "+ReflectorClassName+u8"::GenerateClass()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"    CClass* BaseClass="+Class->BaseClassName+u8"::StaticClass();\n";
            CppFileContent += u8"    CNativeClass* Field = new TClass<"+Class->Name+u8">( U\""+Namespace+u8"::"+ Class->Name+u8"\",BaseClass,"+ReflectorClassName+u8"::PostConstructField);\n";
            //CppFileContent += u8"    Field->PostConstructFunction2=[Field](){ Field->SetOwnerLibrary(U\""+Namespace+u8"\");\n Field->SharedPtrType->SetOwnerLibrary(U\""+Namespace+u8"\");\n Field->WeakPtrType->SetOwnerLibrary(U\""+Namespace+u8"\");\n };\n";
            CppFileContent += u8"    return Field;\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"void "+ReflectorClassName+u8"::PostConstructField()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"    CNativeClass* Field=GetStaticClass();\n";
            CppFileContent += u8"    CPropertyList::CopyProperties(Field->BaseType->Properties,Field->Properties);\n";
            CppFileContent += u8"    Field->Functions->VirtualFunctionTable=Field->BaseType->Functions->VirtualFunctionTable;\n";
            
            CppFileContent += u8"    Field->NewScriptNativeInstanceFunc="+ScriptBaseClassName+u8"::CreateInstance;\n";
            
            //create shared ptr type 
            CppFileContent += u8"    Field->SharedPtrType="+Class->Name+u8"::StaticSharedPtrType();\n";
            CppFileContent += u8"    Field->WeakPtrType="+Class->Name+u8"::StaticWeakPtrType();\n";
           
            //set interfaces
            // if(Class->Meta.contains(u8"Interfaces"))
            // {
            //     auto Interfaces=std::get<std::vector<std::u8string>>(Class->Meta[u8"Interfaces"].value());
            //     for(auto& Interface:Interfaces)
            //     {
            //         CppFileContent += u8"    Field->Interfaces.Add("+Interface+u8"::StaticClass());\n";
            //     }
            // }

            if(!AllFunctions.empty())
            {
                CppFileContent += AllFunctions;
            }
            CppFileContent += AllFields;
            CppFileContent += AllProperties;


            
            CppFileContent += u8"}\n";

            CppFileContent += u8"static uint8_t "+Class->Name+u8"_Register=(" +ReflectorClassName+u8"::GetStaticClass(),0);\n";

            CppFileContent += u8"CClass* "+Class->Name+u8"::StaticClass()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"    return "+ReflectorClassName+u8"::GetStaticClass();\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CClass* "+Class->Name+u8"::GetClass() const \n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"    return StaticClass();\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CType* "+Class->Name+u8"::StaticSharedPtrType()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"  return WH::TReflectionType<TSharedObjectPtr<"+Class->Name+u8">>::FindOrCreateType();\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CType* "+Class->Name+u8"::GetSharedPtrType() const  \n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"    return StaticSharedPtrType();\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CType* "+Class->Name+u8"::StaticWeakPtrType()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"  return WH::TReflectionType<TWeakObjectPtr<"+Class->Name+u8">>::FindOrCreateType();\n";
            CppFileContent += u8"}\n";
        
            CppFileContent += u8"CType* "+Class->Name+u8"::GetWeakPtrType() const \n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"    return StaticWeakPtrType();\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CType* CGetType<" +Class->Name+u8">:: operator () ()";
            CppFileContent += u8"{\n";
            CppFileContent += u8"    return "+Class->Name+u8"::StaticClass();\n";
            CppFileContent += u8"}\n";


            if(Class->ImplementedInterfaces.size()>0)
            {
                CppFileContent += u8"TSharedObjectPtr<RObject> "+Class->Name+u8"::_GetObjectOfInterface() \n";
                CppFileContent += u8"{\n";
                CppFileContent += u8"    return TSharedObjectPtr<RObject>(this);\n";
                CppFileContent += u8"}\n";

                for(auto& Interface:Class->ImplementedInterfaces)
                {
                    //remove I from interface name
                    auto InterfaceWithoutI=Interface.substr(1);
                    CppFileContent += u8"TSharedInterfacePtr<"+Interface+u8"> "+Class->Name+u8"::To"+InterfaceWithoutI+u8"() \n";
                    CppFileContent += u8"{\n";
                    CppFileContent += u8"    return TSharedInterfacePtr<"+Interface+u8">(reinterpret_cast<"+Interface+u8"*>(this));\n";
                    CppFileContent += u8"}\n";
                }
            }   
        }
        else if(Class->IsStruct()  )
        {
            CppFileContent += u8"CNativeStrut* "+ReflectorClassName+u8"::StaticStruct()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"  static CNativeStrut* StaticStruct=new TStruct<" + Class->Name + u8">(U\"" +Namespace+u8"::"+ Class->Name + u8"\","+ ReflectorClassName+u8"::PostConstructField);\n";
            //CppFileContent += u8"  StaticStruct->PostConstructFunction2=[](){ StaticStruct->SetOwnerLibrary(U\""+Namespace+u8"\"); };\n";
            CppFileContent += u8"  return StaticStruct;\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CNativeStrut* "+Class->Name+u8"::GetStruct()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"  return "+Class->Name+u8"::StaticStruct();\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CNativeStrut* "+Class->Name+u8"::StaticStruct()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"  static CNativeStrut* Struct= "+ReflectorClassName+u8"::StaticStruct();\n";
            CppFileContent += u8"  return Struct;\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CType* CGetType<"+Class->Name+u8">::operator () ()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"  return "+Class->Name+u8"::StaticStruct();\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"void "+ReflectorClassName+u8"::PostConstructField()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"   CNativeStrut* Field=StaticStruct();\n";

            if(!AllFunctions.empty())
            {
                CppFileContent += u8"    Field->Functions=new CFunctionList(Field);\n";
                CppFileContent += AllFunctions;
            }
            CppFileContent += AllFields;
            CppFileContent += AllProperties;
            
            CppFileContent += u8"}\n";

            CppFileContent += u8"static uint8_t "+Class->Name+u8"_Register=("+ReflectorClassName+u8"::StaticStruct(),0);\n";
        }
        else if(Class->IsInterface())
        {
            CppFileContent += u8"CInterface* "+ReflectorClassName+u8"::StaticInterface()\n";
            CppFileContent += u8"{\n";

            CppFileContent += u8"  static CInterface* Interface=new TInterface<"+Class->Name+u8">(STypeID(U\""+Namespace+u8"::"+Class->Name+u8"\",true),ETypeFlags::None,"+ReflectorClassName+u8"::PostConstructField);\n";
            //CppFileContent += u8"  Interface->PostConstructFunction2=[](){ Interface->SetOwnerLibrary(U\""+Namespace+u8"\"); };\n";
            CppFileContent += u8"  return Interface;\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CInterface* "+Class->Name+u8"::GetInterface()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"  return "+Class->Name+u8"::StaticInterface();\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CInterface* "+Class->Name+u8"::StaticInterface()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"  static CInterface* Interface= "+ReflectorClassName+u8"::StaticInterface();\n";
            CppFileContent += u8"  return Interface;\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"CType* CGetType<"+Class->Name+u8">::operator () ()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"  return "+Class->Name+u8"::StaticInterface();\n";
            CppFileContent += u8"}\n";

            CppFileContent += u8"void "+ReflectorClassName+u8"::PostConstructField()\n";
            CppFileContent += u8"{\n";
            CppFileContent += u8"   CInterface* Field=StaticInterface();\n";

            if(!AllFunctions.empty())
            {
                CppFileContent += u8"    Field->Functions=new CFunctionList(Field);\n";
                CppFileContent += AllFunctions;
            }

            CppFileContent += AllFields;
            CppFileContent += AllProperties;

            CppFileContent += u8"}\n";

            CppFileContent += u8"static uint8_t "+Class->Name+u8"_Register=("+ReflectorClassName+u8"::StaticInterface(),0);\n";

        }
    }

    if(!Enums.empty())
    {
        for(auto& Enum:Enums )
        {   
            CppFileContent+=u8"CType* CreateEnumnType_"+Enum->Name+u8"()\n";
            CppFileContent+=u8"{\n";

            //because 0-9 is ascii code, so we can convert to u8string directly
            std::u8string EnumValueNum=
                (char8_t*)std::to_string(Enum->Values.size()).c_str();

            CppFileContent+=u8"  TMap<int64_t,CString> Values("+ EnumValueNum+u8");\n";
            for(auto& Value:Enum->Values)
            {
                CppFileContent+=u8"  Values.Add(int64_t("+Enum->Name+u8"::"+Value+u8"),CString(U\""+Value+u8"\"));\n";
            }
            CppFileContent+=u8"  auto ReturnType=new CNativeEnum<"+Enum->Name+u8">(U\""+Namespace+u8"::"+ Enum->Name+u8"\",Values);\n";
            //CppFileContent+=u8"  ReturnType->SetOwnerLibrary(U\""+Namespace+u8"\");\n";
            CppFileContent+=u8"  return ReturnType;\n";
            CppFileContent+=u8"}\n";

            CppFileContent+=u8"CType* CGetType<"+Enum->Name+u8">::operator () () \n";
            CppFileContent+=u8"{\n";
            
            CppFileContent+=u8"  static CType* Type=CreateEnumnType_"+Enum->Name+u8"();\n";
            CppFileContent+=u8"  return Type;\n";

            CppFileContent+=u8"}\n";

        }

    }
}
